package com.rambler.common.config;

import lombok.extern.slf4j.Slf4j;
import net.i2p.crypto.eddsa.EdDSAEngine;
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Base64;

@Component
@Slf4j
public class QWeatherJwtProvider {

    @Value("${weather.qweather-api.config.key-id}")
    private String keyId;

    @Value("${weather.qweather-api.config.project-id}")
    private String projectId;

    @Value("${weather.qweather-api.config.private-key-path}")
    private String privateKeyPath;

    @Value("${weather.qweather-api.jwt-config.expire-minutes}")
    private Integer expireMinutes;

    @Value("${weather.qweather-api.jwt-config.redis-key}")
    private String jwtRedisKey;

    private String cachedToken;
    private Instant tokenExpireAt;

    public String getToken() {
        if (cachedToken == null || Instant.now().isAfter(tokenExpireAt)) {
            try {
                log.info("qweather: token已过期, 重新签发");
                cachedToken = generateJwtToken();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        return cachedToken;
    }

    /**
     * 生成token
     *
     * @return jwt token
     * @throws Exception 文件读取不到异常
     */
    private String generateJwtToken() throws Exception {
        // 读取私钥
        String privateKeyString = readPrivateKey(privateKeyPath);
        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyString);
        PKCS8EncodedKeySpec encoded = new PKCS8EncodedKeySpec(privateKeyBytes);
        PrivateKey privateKey = new EdDSAPrivateKey(encoded);

        // 配置token
        // Header
        String headerJson = "{\"alg\": \"EdDSA\", \"kid\": \"" + keyId + "\"}";

        // Payload(30秒是防止和风天气比我们的有延迟, 导致我这里签发的token被认为还没生效)
        long iat = ZonedDateTime.now(ZoneOffset.UTC).toEpochSecond() - 30;
        long exp = iat + expireMinutes * 60;
        String payloadJson = "{\"sub\": \"" + projectId + "\", \"iat\": " + iat + ", \"exp\": " + exp + "}";

        // Base64url header+payload
        String headerEncoded = Base64.getUrlEncoder().encodeToString(headerJson.getBytes(StandardCharsets.UTF_8));
        String payloadEncoded = Base64.getUrlEncoder().encodeToString(payloadJson.getBytes(StandardCharsets.UTF_8));
        String data = headerEncoded + "." + payloadEncoded;

        EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519);

        // Sign
        final Signature s = new EdDSAEngine(MessageDigest.getInstance(spec.getHashAlgorithm()));
        s.initSign(privateKey);
        s.update(data.getBytes(StandardCharsets.UTF_8));
        byte[] signature = s.sign();

        String signatureString = Base64.getUrlEncoder().encodeToString(signature);
        tokenExpireAt = Instant.ofEpochSecond(exp);
        return data + "." + signatureString;
    }

    /**
     * 从 PEM 文件中读取私钥内容（去掉头尾）
     *
     * @param filePath 私钥文件路径，例如 "/path/to/private_key.pem"
     * @return 私钥的 base64 编码字符串（无头尾）
     * @throws IOException 文件读取失败时抛出
     */
    public static String readPrivateKey(String filePath) throws IOException {
        // 读取整个文件内容
        String key = new String(Files.readAllBytes(Paths.get(filePath)));

        // 移除头尾标识
        key = key
                .replace("-----BEGIN PRIVATE KEY-----", "")
                .replace("-----END PRIVATE KEY-----", "")
                .replaceAll("\\s+", ""); // 去掉所有空格和换行符

        return key;
    }

}
